﻿namespace Algo.Tests
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using Algo.Sort;

    using Moq;

    using NUnit.Framework;

    using Shouldly;

    [TestFixture]
    public class SortStrategyFixture
    {
        private readonly IEnumerable<int> _items = Array.AsReadOnly(new[] { 5, 2, 3, 1, 4 });
        private readonly IEnumerable<int> _itemsForBadCase = Array.AsReadOnly(new[] { 5, 4, 3, 2, 1 });
        private readonly IEnumerable<int> _expectedSortedItems = Array.AsReadOnly(new[] { 1, 2, 3, 4, 5 });
        private Mock<IMetricsExtension> _metrics;
        private Mock<IAlgoExtensions> _behaviours;
        private int _iterationsCount;

        [SetUp]
        public void SetUp()
        {
            _iterationsCount = 0;
            _metrics = new Mock<IMetricsExtension>();
            _metrics.Setup(i => i.RegisterIterations(It.IsAny<int>()))
                .Callback<int>(
                    count =>
                    {
                        lock (_metrics)
                        {
                            _iterationsCount += count;
                        }
                    });

            var sortingMetricsObject = _metrics.Object;
            _behaviours = new Mock<IAlgoExtensions>();
            _behaviours.Setup(i => i.TryGet(out sortingMetricsObject)).Returns(true);
        }

        [Test]
        [TestCase(typeof(SelectionSortStrategy), 10)]
        [TestCase(typeof(BubbleSortStrategy), 6)]
        [TestCase(typeof(CocktailSortStrategy), 6)]
        [TestCase(typeof(GnomeSortStrategy), 10)]
        [TestCase(typeof(InsertsSortStrategy), 5)]
        [TestCase(typeof(MergeSortStrategy), 8)]
        [TestCase(typeof(TreeSortStrategy), 8)]
        [TestCase(typeof(QuickSortStrategy), 4)]
        public void ShouldSort(Type type, int iterationsCount)
        {
            // Given
            var target = CreateInstance(type);

            // When
            var actualSortedItems = target.Sort(_items, Comparer<int>.Default, _behaviours.Object);

            // Then
            actualSortedItems.ShouldBe(_expectedSortedItems);
            _iterationsCount.ShouldBe(iterationsCount);
        }

        [Test]
        [TestCase(typeof(SelectionSortStrategy), 10)]
        [TestCase(typeof(BubbleSortStrategy), 10)]
        [TestCase(typeof(CocktailSortStrategy), 6)]
        [TestCase(typeof(GnomeSortStrategy), 14)]
        [TestCase(typeof(InsertsSortStrategy), 4)]
        [TestCase(typeof(MergeSortStrategy), 5)]
        [TestCase(typeof(TreeSortStrategy), 10)]
        [TestCase(typeof(QuickSortStrategy), 4)]
        public void ShouldSortForBadCase(Type type, int iterationsCount)
        {
            // Given
            var target = CreateInstance(type);

            // When
            var actualSortedItems = target.Sort(_itemsForBadCase, Comparer<int>.Default, _behaviours.Object);

            // Then
            actualSortedItems.ShouldBe(_expectedSortedItems);
            _iterationsCount.ShouldBe(iterationsCount);
        }

        [Test]
        [TestCase(typeof(SelectionSortStrategy))]
        [TestCase(typeof(BubbleSortStrategy))]
        [TestCase(typeof(CocktailSortStrategy))]
        [TestCase(typeof(GnomeSortStrategy))]
        [TestCase(typeof(InsertsSortStrategy))]
        [TestCase(typeof(MergeSortStrategy))]
        [TestCase(typeof(TreeSortStrategy))]
        [TestCase(typeof(QuickSortStrategy))]
        public void ShouldSortEmptySet(Type type)
        {
            // Given
            var target = CreateInstance(type);

            // When
            var actualSortedItems = target.Sort(new int[0], Comparer<int>.Default, _behaviours.Object);

            // Then
            actualSortedItems.Count().ShouldBe(0);
            _iterationsCount.ShouldBe(0);
        }

        [Test]
        [TestCase(typeof(SelectionSortStrategy))]
        [TestCase(typeof(BubbleSortStrategy))]
        [TestCase(typeof(CocktailSortStrategy))]
        [TestCase(typeof(GnomeSortStrategy))]
        [TestCase(typeof(InsertsSortStrategy))]
        [TestCase(typeof(MergeSortStrategy))]
        [TestCase(typeof(TreeSortStrategy))]
        [TestCase(typeof(QuickSortStrategy))]
        public void ShouldSortOneItem(Type type)
        {
            // Given
            var target = CreateInstance(type);

            // When
            var actualSortedItems = target.Sort(new[] { 1 }, Comparer<int>.Default, _behaviours.Object);

            // Then
            // ReSharper disable PossibleMultipleEnumeration
            actualSortedItems.Count().ShouldBe(1);
            actualSortedItems.First().ShouldBe(1);
            // ReSharper restore PossibleMultipleEnumeration
            _iterationsCount.ShouldBe(0);
        }

        private static ISortStrategy CreateInstance(Type type)
        {
            return (ISortStrategy)Activator.CreateInstance(type);
        }
    }
}
